package soeasy.model;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.Properties;

import org.jfree.data.Range;

import rast.SimulationEnvironment;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.IAction;
import repast.simphony.engine.schedule.ISchedulableAction;
import repast.simphony.engine.schedule.ISchedule;
import repast.simphony.engine.schedule.ScheduleParameters;

/**
 * This class is to separate the codes of the agent-based model and the RePast
 * framework.
 * 
 * @author Önder Gürcan
 * 
 */
public class SOEASYParameters extends SimulationEnvironment {

	private static SOEASYParameters instance;

	private boolean isStable = false;

	private boolean isSendingFeedback = true;	

	private String reportsFolder = null;

	public static final String REORGANIZATION_ANNYOYANCE_LEVEL = "reorganization_annoyance_level";
	public static final String EVOLUTION_ANNYOYANCE_LEVEL = "evolution_annoyance_level";
	public static final String NUMBER_OF_INITIAL_SENSORY_NEURONS = "number_of_initial_sensory_neurons";
	public static final String HELPING_DISTANCE = "helping_distance";
	public static final String RESTING_AHP_LEVEL = "resting_ahp_level";
	public static final String DERIVATIVE_FAULT_TOLERANCE = "derivative_fault_tolerance";
	public static final String PATHWAY_BEGINNING = "pathway_beginning";
	public static final String PATHWAY_END = "pathway_end";
	public static final String STIMULUS_OCCURANCE = "stimulus_occurance";
	public static final String CONSECUTIVE_STIMULUS_DELAY = "consecutive_stimulus_delay";

	protected SOEASYParameters() {
		super();
	}

	@Override
	protected void initializeParameters() {
		this.parameters = new Properties();
		this.parameters.setProperty(REORGANIZATION_ANNYOYANCE_LEVEL, "20.0");
		this.parameters.setProperty(EVOLUTION_ANNYOYANCE_LEVEL, "40.0");
		this.parameters.setProperty(NUMBER_OF_INITIAL_SENSORY_NEURONS, "1.0");
		this.parameters.setProperty(HELPING_DISTANCE, "4.0");
		this.parameters.setProperty(RESTING_AHP_LEVEL, "0.10");
		this.parameters.setProperty(DERIVATIVE_FAULT_TOLERANCE, "0.50");		
		this.parameters.setProperty(PATHWAY_BEGINNING, "40.0");
		this.parameters.setProperty(PATHWAY_END, "200.0");
		this.parameters.setProperty(STIMULUS_OCCURANCE, "1.0");
		this.parameters.setProperty(CONSECUTIVE_STIMULUS_DELAY, "25.0");
	}

	/**
	 * Returns the instance of the singleton SimulationEnvironment class.
	 * 
	 * @return
	 */
	public static SOEASYParameters getInstance() {
		if (instance == null) {
			instance = new SOEASYParameters();
		}

		return instance;
	}

	public double getCurrentTick() {
		double currentTick = RunEnvironment.getInstance().getCurrentSchedule()
				.getTickCount();
		// currentTick = MathUtils.digitalize(currentTick);
		return currentTick;
	}

	public String getCurrentTickAsString() {
		return new DecimalFormat("000000000000.0").format(getCurrentTick());
	}

	public double getNumberOfActions() {
		return RunEnvironment.getInstance().getCurrentSchedule()
				.getActionCount();
	}

	public static ISchedulableAction schedule(
			ScheduleParameters scheduleParams, IAction action) {
		ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
		ISchedulableAction schedulableAction = schedule.schedule(
				scheduleParams, action);
		return schedulableAction;
	}

	/**
	 * Returns the properties of SO-EASY simulation model.
	 * 
	 * @return
	 */
	public double getParameter(String name) {
		String parameter = this.parameters.getProperty(name);
		return Double.parseDouble(parameter);
	}

	public void setParameter(String name, String value) {
		this.parameters.setProperty(name, value);

		// re-set reference psf
	
	//	this.referencePSF = null;

//		if (name.equals(MNDISCHARGE_DATA_FILE)) {
////			DoubleArrayList triggers = getReferencePSF().getTriggers();
////			double firstTrigger = triggers.get(0);
////			double lastTrigger = triggers.lastElement();
////			double triggerDuration = Math.round(lastTrigger - firstTrigger);
////			this.parameters.setProperty(TRIGGER_DURATION, "" + triggerDuration);
////			this.parameters.setProperty(TRIGGER_COUNT, "" + triggers.size());
//		} else 
//		if (name.equals(MOVING_AVERAGE_INTERVAL)) {
//			this.referencePSF = null;
//			this.referencePSFCUSUM = null;
		//}
	}

	public double getRestingAhpLevel() {
		String parameter = this.parameters.getProperty(RESTING_AHP_LEVEL);
		return Double.parseDouble(parameter);
	}	

	public void setStable(boolean isStable) {
		this.isStable = isStable;
	}

	public boolean isStable() {
		return isStable;
	}

	public void setSendingFeedback(boolean isSendingFeedback) {
		this.isSendingFeedback = isSendingFeedback;
	}

	public boolean isSendingFeedback() {
		return isSendingFeedback;
	}		
	
	public void setReportsFolder(String reportsFolder) {
		this.reportsFolder = reportsFolder;
	}

	public String getReportsFolder() {
		return reportsFolder;
	}
	
	public Range getTrainingRange() {
		double pathwayBeginning = getParameter(SOEASYParameters.PATHWAY_BEGINNING);
		double pathwayEnd = getParameter(SOEASYParameters.PATHWAY_END);
		return new Range(pathwayBeginning, pathwayEnd);
	}

	public void saveToFile(String fileName) throws IOException {
		BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(
				fileName));
		Iterator<String> iterator = this.parameters.stringPropertyNames()
				.iterator();
		while (iterator.hasNext()) {
			String propertyName = iterator.next();
			String property = this.parameters.getProperty(propertyName);
			bufferedWriter.write(propertyName + " = " + property + "\n");
		}
		bufferedWriter.close();
	}
}
